home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Linux / Kubuntu 8.10 / kubuntu-8.10-desktop-i386.iso / casper / filesystem.squashfs / usr / share / pyshared / GDebi / DebPackage.py < prev    next >
Text File  |  2008-08-05  |  15KB  |  420 lines

  1. # Copyright (c) 2005-2007 Canonical
  2. #
  3. # AUTHOR:
  4. # Michael Vogt <mvo@ubuntu.com>
  5. #
  6. # This file is part of GDebi
  7. #
  8. # GDebi is free software; you can redistribute it and/or
  9. # modify it under the terms of the GNU General Public License as published
  10. # by the Free Software Foundation; either version 2 of the License, or (at
  11. # your option) any later version.
  12. #
  13. # GDebi is distributed in the hope that it will be useful,
  14. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  16. # General Public License for more details.
  17. #
  18. # You should have received a copy of the GNU General Public License
  19. # along with GDebi; if not, write to the Free Software
  20. # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  21. #
  22.  
  23. import warnings
  24. from warnings import warn
  25. warnings.filterwarnings("ignore", "apt API not stable yet", FutureWarning)
  26. import apt_inst, apt_pkg
  27. import apt
  28. import sys
  29. import os
  30. from gettext import gettext as _
  31. from Cache import Cache
  32.  
  33. class DebPackage(object):
  34.     debug = 0
  35.  
  36.     def __init__(self, cache, file=None):
  37.         cache.clear()
  38.         self._cache = cache
  39.         self.file = file
  40.         self._needPkgs = []
  41.         self._sections = {}
  42.         self._installedConflicts = set()
  43.         self._failureString = ""
  44.         if file != None:
  45.             self.open(file)
  46.             
  47.     def open(self, file):
  48.         """ read a deb """
  49.         control = apt_inst.debExtractControl(open(file))
  50.         self._sections = apt_pkg.ParseSection(control)
  51.         self.pkgName = self._sections["Package"]
  52.  
  53.     def _isOrGroupSatisfied(self, or_group):
  54.         """ this function gets a 'or_group' and analyzes if
  55.             at least one dependency of this group is already satisfied """
  56.         self._dbg(2,"_checkOrGroup(): %s " % (or_group))
  57.  
  58.         for dep in or_group:
  59.             depname = dep[0]
  60.             ver = dep[1]
  61.             oper = dep[2]
  62.  
  63.             # check for virtual pkgs
  64.             if not self._cache.has_key(depname):
  65.                 if self._cache.isVirtualPkg(depname):
  66.                     self._dbg(3,"_isOrGroupSatisfied(): %s is virtual dep" % depname)
  67.                     for pkg in self._cache.getProvidersForVirtual(depname):
  68.                         if pkg.isInstalled:
  69.                             return True
  70.                 continue
  71.  
  72.             inst = self._cache[depname]
  73.             instver = inst.installedVersion
  74.             if instver != None and apt_pkg.CheckDep(instver,oper,ver) == True:
  75.                 return True
  76.         return False
  77.             
  78.  
  79.     def _satisfyOrGroup(self, or_group):
  80.         """ try to satisfy the or_group """
  81.  
  82.         or_found = False
  83.         virtual_pkg = None
  84.  
  85.         for dep in or_group:
  86.             depname = dep[0]
  87.             ver = dep[1]
  88.             oper = dep[2]
  89.  
  90.             # if we don't have it in the cache, it may be virtual
  91.             if not self._cache.has_key(depname):
  92.                 if not self._cache.isVirtualPkg(depname):
  93.                     continue
  94.                 providers = self._cache.getProvidersForVirtual(depname)
  95.                 # if a package just has a single virtual provider, we
  96.                 # just pick that (just like apt)
  97.                 if len(providers) != 1:
  98.                     continue
  99.                 depname = providers[0].name
  100.                 
  101.             # now check if we can satisfy the deps with the candidate(s)
  102.             # in the cache
  103.             cand = self._cache[depname]
  104.             candver = self._cache._depcache.GetCandidateVer(cand._pkg)
  105.             if not candver:
  106.                 continue
  107.             if not apt_pkg.CheckDep(candver.VerStr,oper,ver):
  108.                 continue
  109.  
  110.             # check if we need to install it
  111.             self._dbg(2,"Need to get: %s" % depname)
  112.             self._needPkgs.append(depname)
  113.             return True
  114.  
  115.         # if we reach this point, we failed
  116.         or_str = ""
  117.         for dep in or_group:
  118.             or_str += dep[0]
  119.             if dep != or_group[len(or_group)-1]:
  120.                 or_str += "|"
  121.         self._failureString += _("Dependency is not satisfiable: %s\n" % or_str)
  122.         return False
  123.  
  124.     def _checkSinglePkgConflict(self, pkgname, ver, oper):
  125.         """ returns true if a pkg conflicts with a real installed/marked
  126.             pkg """
  127.         # FIXME: deal with conflicts against its own provides
  128.         #        (e.g. Provides: ftp-server, Conflicts: ftp-server)
  129.         self._dbg(3, "_checkSinglePkgConflict() pkg='%s' ver='%s' oper='%s'" % (pkgname, ver, oper))
  130.         pkgver = None
  131.         cand = self._cache[pkgname]
  132.         if cand.isInstalled:
  133.             pkgver = cand.installedVersion
  134.         elif cand.markedInstall:
  135.             pkgver = cand.candidateVersion
  136.         #print "pkg: %s" % pkgname
  137.         #print "ver: %s" % ver
  138.         #print "pkgver: %s " % pkgver
  139.         #print "oper: %s " % oper
  140.         if (pkgver and apt_pkg.CheckDep(pkgver,oper,ver) and 
  141.             not self.replacesRealPkg(pkgname, oper, ver)):
  142.             self._failureString += _("Conflicts with the installed package '%s'" % cand.name)
  143.             return True
  144.         return False
  145.  
  146.     def _checkConflictsOrGroup(self, or_group):
  147.         """ check the or-group for conflicts with installed pkgs """
  148.         self._dbg(2,"_checkConflictsOrGroup(): %s " % (or_group))
  149.  
  150.         or_found = False
  151.         virtual_pkg = None
  152.  
  153.         for dep in or_group:
  154.             depname = dep[0]
  155.             ver = dep[1]
  156.             oper = dep[2]
  157.  
  158.             # check conflicts with virtual pkgs
  159.             if not self._cache.has_key(depname):
  160.                 # FIXME: we have to check for virtual replaces here as 
  161.                 #        well (to pass tests/gdebi-test8.deb)
  162.                 if self._cache.isVirtualPkg(depname):
  163.                     for pkg in self._cache.getProvidersForVirtual(depname):
  164.                         self._dbg(3, "conflicts virtual check: %s" % pkg.name)
  165.                         # P/C/R on virtal pkg, e.g. ftpd
  166.                         if self.pkgName == pkg.name:
  167.                             self._dbg(3, "conflict on self, ignoring")
  168.                             continue
  169.                         if self._checkSinglePkgConflict(pkg.name,ver,oper):
  170.                             self._installedConflicts.add(pkg.name)
  171.                 continue
  172.             if self._checkSinglePkgConflict(depname,ver,oper):
  173.                 self._installedConflicts.add(depname)
  174.         return len(self._installedConflicts) != 0
  175.  
  176.     def getConflicts(self):
  177.         conflicts = []
  178.         key = "Conflicts"
  179.         if self._sections.has_key(key):
  180.             conflicts = apt_pkg.ParseDepends(self._sections[key])
  181.         return conflicts
  182.  
  183.     def getDepends(self):
  184.         depends = []
  185.         # find depends
  186.         for key in ["Depends","PreDepends"]:
  187.             if self._sections.has_key(key):
  188.                 depends.extend(apt_pkg.ParseDepends(self._sections[key]))
  189.         return depends
  190.  
  191.     def getProvides(self):
  192.         provides = []
  193.         key = "Provides"
  194.         if self._sections.has_key(key):
  195.             provides = apt_pkg.ParseDepends(self._sections[key])
  196.         return provides
  197.  
  198.     def getReplaces(self):
  199.         replaces = []
  200.         key = "Replaces"
  201.         if self._sections.has_key(key):
  202.             replaces = apt_pkg.ParseDepends(self._sections[key])
  203.         return replaces
  204.  
  205.     def replacesRealPkg(self, pkgname, oper, ver):
  206.         """ 
  207.         return True if the deb packages replaces a real (not virtual)
  208.         packages named pkgname, oper, ver 
  209.         """
  210.         self._dbg(3, "replacesPkg() %s %s %s" % (pkgname,oper,ver))
  211.         pkgver = None
  212.         cand = self._cache[pkgname]
  213.         if cand.isInstalled:
  214.             pkgver = cand.installedVersion
  215.         elif cand.markedInstall:
  216.             pkgver = cand.candidateVersion
  217.         for or_group in self.getReplaces():
  218.             for (name, ver, oper) in or_group:
  219.                 if (name == pkgname and 
  220.                     apt_pkg.CheckDep(pkgver,oper,ver)):
  221.                     self._dbg(3, "we have a replaces in our package for the conflict against '%s'" % (pkgname))
  222.                     return True
  223.         return False
  224.  
  225.     def checkConflicts(self):
  226.         """ check if the pkg conflicts with a existing or to be installed
  227.             package. Return True if the pkg is ok """
  228.         res = True
  229.         for or_group in self.getConflicts():
  230.             if self._checkConflictsOrGroup(or_group):
  231.                 #print "Conflicts with a exisiting pkg!"
  232.                 #self._failureString = "Conflicts with a exisiting pkg!"
  233.                 res = False
  234.         return res
  235.  
  236.     # some constants
  237.     (NO_VERSION,
  238.      VERSION_OUTDATED,
  239.      VERSION_SAME,
  240.      VERSION_IS_NEWER) = range(4)
  241.     
  242.     def compareToVersionInCache(self, useInstalled=True):
  243.         """ checks if the pkg is already installed or availabe in the cache
  244.             and if so in what version, returns if the version of the deb
  245.             is not available,older,same,newer
  246.         """
  247.         self._dbg(3,"compareToVersionInCache")
  248.         pkgname = self._sections["Package"]
  249.         debver = self._sections["Version"]
  250.         self._dbg(1,"debver: %s" % debver)
  251.         if self._cache.has_key(pkgname):
  252.             if useInstalled:
  253.                 cachever = self._cache[pkgname].installedVersion
  254.             else:
  255.                 cachever = self._cache[pkgname].candidateVersion
  256.             if cachever != None:
  257.                 cmp = apt_pkg.VersionCompare(cachever,debver)
  258.                 self._dbg(1, "CompareVersion(debver,instver): %s" % cmp)
  259.                 if cmp == 0:
  260.                     return self.VERSION_SAME
  261.                 elif cmp < 0:
  262.                     return self.VERSION_IS_NEWER
  263.                 elif cmp > 0:
  264.                     return self.VERSION_OUTDATED
  265.         return self.NO_VERSION
  266.  
  267.     def checkDeb(self):
  268.         self._dbg(3,"checkDepends")
  269.  
  270.         # check arch
  271.         arch = self._sections["Architecture"]
  272.         if  arch != "all" and arch != apt_pkg.Config.Find("APT::Architecture"):
  273.             self._dbg(1,"ERROR: Wrong architecture dude!")
  274.             self._failureString = _("Wrong architecture '%s'" % arch)
  275.             return False
  276.  
  277.         # check version
  278.         res = self.compareToVersionInCache()
  279.         if res == self.VERSION_OUTDATED: # the deb is older than the installed
  280.             self._failureString = _("A later version is already installed")
  281.             return False
  282.  
  283.         # FIXME: this sort of error handling sux
  284.         self._failureString = ""
  285.  
  286.         # check conflicts
  287.         if not self.checkConflicts():
  288.             return False
  289.  
  290.         # try to satisfy the dependencies
  291.         res = self._satisfyDepends(self.getDepends())
  292.         if not res:
  293.             return False
  294.  
  295.         # check for conflicts again (this time with the packages that are
  296.         # makeed for install)
  297.         if not self.checkConflicts():
  298.             return False
  299.  
  300.         if self._cache._depcache.BrokenCount > 0:
  301.             self._failureString = _("Failed to satisfy all dependencies (broken cache)")
  302.             # clean the cache again
  303.             self._cache.clear()
  304.             return False
  305.         return True
  306.  
  307.     def satisfyDependsStr(self, dependsstr):
  308.         return self._satisfyDepends(apt_pkg.ParseDepends(dependsstr))
  309.  
  310.     def _satisfyDepends(self, depends):
  311.         # turn off MarkAndSweep via a action group (if available)
  312.         try:
  313.             _actiongroup = apt_pkg.GetPkgActionGroup(self._cache._depcache)
  314.         except AttributeError, e:
  315.             pass
  316.         # check depends
  317.         for or_group in depends:
  318.             #print "or_group: %s" % or_group
  319.             #print "or_group satified: %s" % self._isOrGroupSatisfied(or_group)
  320.             if not self._isOrGroupSatisfied(or_group):
  321.                 if not self._satisfyOrGroup(or_group):
  322.                     return False
  323.         # now try it out in the cache
  324.             for pkg in self._needPkgs:
  325.                 try:
  326.                     self._cache[pkg].markInstall(fromUser=False)
  327.                 except SystemError, e:
  328.                     self._failureString = _("Cannot install '%s'" % pkg)
  329.                     self._cache.clear()
  330.                     return False
  331.         return True
  332.  
  333.     def missingDeps(self):
  334.         self._dbg(1, "Installing: %s" % self._needPkgs)
  335.         if self._needPkgs == None:
  336.             self.checkDeb()
  337.         return self._needPkgs
  338.     missingDeps = property(missingDeps)
  339.  
  340.     def requiredChanges(self):
  341.         """ gets the required changes to satisfy the depends.
  342.             returns a tuple with (install, remove, unauthenticated)
  343.         """
  344.         install = []
  345.         remove = []
  346.         unauthenticated = []
  347.         for pkg in self._cache:
  348.             if pkg.markedInstall or pkg.markedUpgrade:
  349.                 install.append(pkg.name)
  350.                 # check authentication, one authenticated origin is enough
  351.                 # libapt will skip non-authenticated origins then
  352.                 authenticated = False
  353.                 for origin in pkg.candidateOrigin:
  354.                     authenticated |= origin.trusted
  355.                 if not authenticated:
  356.                     unauthenticated.append(pkg.name)
  357.             if pkg.markedDelete:
  358.                 remove.append(pkg.name)
  359.         return (install,remove, unauthenticated)
  360.     requiredChanges = property(requiredChanges)
  361.  
  362.     def filelist(self):
  363.         """ return the list of files in the deb """
  364.         files = []
  365.         def extract_cb(What,Name,Link,Mode,UID,GID,Size,MTime,Major,Minor):
  366.             #print "%s '%s','%s',%u,%u,%u,%u,%u,%u,%u"\
  367.             #      % (What,Name,Link,Mode,UID,GID,Size, MTime, Major, Minor)
  368.             files.append(Name)
  369.         try:
  370.             try:
  371.                 apt_inst.debExtract(open(self.file), extract_cb, "data.tar.gz")
  372.             except SystemError, e:
  373.                 try:
  374.                     apt_inst.debExtract(open(self.file), extract_cb, "data.tar.bz2")
  375.                 except SystemError, e:
  376.                     try:
  377.                         apt_inst.debExtract(open(self.file), extract_cb, "data.tar.lzma")
  378.                     except SystemError, e:
  379.                         return [_("List of files could not be read, please report this as a bug")]
  380.         # IOError may happen because of gvfs madness (LP: #211822)
  381.         except IOError, e:
  382.             return [_("IOError during filelist read: %s" % e)]
  383.         return files
  384.     filelist = property(filelist)
  385.     
  386.     # properties
  387.     def __getitem__(self,item):
  388.         if not self._sections.has_key(item):
  389.             # Translators: it's for missing entries in the deb package,
  390.             # e.g. a missing "Maintainer" field
  391.             return _("%s is not available" % item)
  392.         return self._sections[item]
  393.  
  394.     def _dbg(self, level, msg):
  395.         """Write debugging output to sys.stderr.
  396.         """
  397.         if level <= self.debug:
  398.             print >> sys.stderr, msg
  399.  
  400.  
  401. if __name__ == "__main__":
  402.  
  403.     cache = Cache()
  404.  
  405.     vp = "www-browser"
  406.     print "%s virtual: %s" % (vp,cache.isVirtualPkg(vp))
  407.     providers = cache.getProvidersForVirtual(vp)
  408.     print "Providers for %s :" % vp
  409.     for pkg in providers:
  410.         print " %s" % pkg.name
  411.     
  412.     d = DebPackage(cache, sys.argv[1])
  413.     print "Deb: %s" % d.pkgName
  414.     if not d.checkDeb():
  415.         print "can't be satified"
  416.         print d._failureString
  417.     print "missing deps: %s" % d.missingDeps
  418.     print d.requiredChanges
  419.  
  420.